﻿using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Timeline.Beans;
using Timeline.Inis;
using Timeline.Providers;
using Timeline.Utils;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Resources;
using Windows.Foundation;
using Windows.Globalization.NumberFormatting;
using Windows.Networking.BackgroundTransfer;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.Storage.FileProperties;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;

namespace Timeline.Pages {
    public sealed partial class SettingsView : UserControl {
        public event EventHandler<SettingsEventArgs> SettingsChanged;
        //public event EventHandler<DlgEventArgs> DlgChanged;

        private Ini ini = new Ini();

        private readonly ResourceLoader resLoader;
        private readonly ApplicationDataContainer localSettings;

        ObservableCollection<Cate> listSnakeCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listGluttonCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listTimelineCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listBingCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listNasaCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listOneCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listQingbzCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listZzzmhCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listNetbianCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listToopicCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listWallhavenCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listWallhereCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listWallpaperupCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listAbyssCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> list3gCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listHuamaoCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listPexelsCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listColorhubCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listSimpleCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listEsoCateWhat = new ObservableCollection<Cate>();
        ObservableCollection<Cate> listYmyouliCateWhat = new ObservableCollection<Cate>();

        private ReleaseApiData release = null;

        private DispatcherTimer himawari8OffsetTimer = null;
        private DispatcherTimer himawari8RatioTimer = null;

        public SettingsView() {
            this.InitializeComponent();

            resLoader = ResourceLoader.GetForCurrentView();
            localSettings = ApplicationData.Current.LocalSettings;
            Init();
        }

        private void Init() {
            TextApp.Text = resLoader.GetString("AppName") + " " + SysUtil.GetPkgVer(false);

            BoxHimawari8Offset.NumberFormatter = new DecimalFormatter {
                IntegerDigits = 1,
                FractionDigits = 2,
                NumberRounder = new IncrementNumberRounder {
                    Increment = 0.01,
                    RoundingAlgorithm = RoundingAlgorithm.RoundHalfUp
                }
            };
            BoxHimawari8Ratio.NumberFormatter = new DecimalFormatter {
                IntegerDigits = 1,
                FractionDigits = 2,
                NumberRounder = new IncrementNumberRounder {
                    Increment = 0.01,
                    RoundingAlgorithm = RoundingAlgorithm.RoundHalfUp
                }
            };
        }

        public void NotifyPaneOpened(Ini ini) {
            this.ini = ini;
            // 内测标记
            if ("54311Bysyk.forWindows11_egefxwh1bkmgg".Equals(Package.Current.Id.FamilyName)) {
                TextSettings.Text = resLoader.GetString("TitleBeta");
            }
            // 控制隐藏图源组是否可用
            ExpanderYmyouli.Visibility = localSettings.Values.ContainsKey("ymyouli") || ExpanderYmyouli.Tag.Equals(ini.Provider)
                ? Visibility.Visible : Visibility.Collapsed;
            ExpanderLsp.Visibility = ini.R18 == 1 || ExpanderLsp.Tag.Equals(ini.Provider)
                ? Visibility.Visible : Visibility.Collapsed;
            ExpanderVenus.Visibility = ini.R18 == 1 || ExpanderVenus.Tag.Equals(ini.Provider)
                ? Visibility.Visible : Visibility.Collapsed;
            TextSettingsProvider3.Visibility = ExpanderLsp.Visibility == Visibility.Visible || ExpanderVenus.Visibility == Visibility.Visible
                ? Visibility.Visible : Visibility.Collapsed;
            // 刷新“图源”组设置项
            GeneralIni gi = ini.GetIni("local");
            GridLocalOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            gi = ini.GetIni("favorite");
            GridFavoriteOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            gi = ini.GetIni("snake");
            GridSnakeOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridSnakeCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxSnakeCateWhat, listSnakeCateWhat, gi);
            gi = ini.GetIni("glutton");
            GridGluttonOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridGluttonCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxGluttonCateWhat, listGluttonCateWhat, gi);
            gi = ini.GetIni("timeline");
            GridTimelineOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            RefreshCateWhat(BoxTimelineCateWhat, listTimelineCateWhat, gi);
            gi = ini.GetIni("bing");
            GridBingOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            RefreshCateWhat(BoxBingCateWhat, listBingCateWhat, gi);
            gi = ini.GetIni("nasa");
            GridNasaOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridNasaCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxNasaCateWhat, listNasaCateWhat, gi);
            gi = ini.GetIni("one");
            GridOneOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridOneCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxOneCateWhat, listOneCateWhat, gi);
            gi = ini.GetIni("himawari8");
            BoxHimawari8Offset.Value = ((Himawari8Ini)ini.GetIni("himawari8")).Offset;
            BoxHimawari8Ratio.Value = ((Himawari8Ini)ini.GetIni("himawari8")).Ratio;
            gi = ini.GetIni("qingbz");
            GridQingbzOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridQingbzCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxQingbzCateWhat, listQingbzCateWhat, gi);
            gi = ini.GetIni("zzzmh");
            GridZzzmhOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridZzzmhCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxZzzmhCateWhat, listZzzmhCateWhat, gi);
            gi = ini.GetIni("netbian");
            GridNetbianOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridNetbianCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxNetbianCateWhat, listNetbianCateWhat, gi);
            gi = ini.GetIni("toopic");
            GridToopicOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridToopicCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxToopicCateWhat, listToopicCateWhat, gi);
            gi = ini.GetIni("wallhaven");
            GridWallhavenOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridWallhavenCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxWallhavenCateWhat, listWallhavenCateWhat, gi);
            gi = ini.GetIni("wallhere");
            GridWallhereOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridWallhereCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxWallhereCateWhat, listWallhereCateWhat, gi);
            gi = ini.GetIni("wallpaperup");
            GridWallpaperupOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridWallpaperupCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxWallpaperupCateWhat, listWallpaperupCateWhat, gi);
            gi = ini.GetIni("abyss");
            GridAbyssOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridAbyssCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxAbyssCateWhat, listAbyssCateWhat, gi);
            gi = ini.GetIni("3g");
            Grid3gOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            Grid3gCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(Box3gCateWhat, list3gCateWhat, gi);
            gi = ini.GetIni("huamao");
            GridHuamaoOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridHuamaoCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxHuamaoCateWhat, listHuamaoCateWhat, gi);
            gi = ini.GetIni("pexels");
            GridPexelsOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            RefreshCateWhat(BoxPexelsCateWhat, listPexelsCateWhat, gi);
            gi = ini.GetIni("colorhub");
            GridColorhubOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            RefreshCateWhat(BoxColorhubCateWhat, listColorhubCateWhat, gi);
            gi = ini.GetIni("simple");
            GridSimpleOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            RefreshCateWhat(BoxSimpleCateWhat, listSimpleCateWhat, gi);
            gi = ini.GetIni("eso");
            GridEsoOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridEsoCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxEsoCateWhat, listEsoCateWhat, gi);
            gi = ini.GetIni("ymyouli");
            GridYmyouliOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridYmyouliCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            RefreshCateWhat(BoxYmyouliCateWhat, listYmyouliCateWhat, gi);
            gi = ini.GetIni("lsp");
            GridLspOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridLspCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            gi = ini.GetIni("venus");
            GridVenusOrder.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.Order));
            GridVenusCateHow.Children.Cast<ToggleButton>().ToList().ForEach(x => x.IsChecked = x.Tag.Equals(gi.CateHow));
            // 刷新推送指示图标
            //foreach (string id in dicPushDesktop.Keys) {
            //    dicPushDesktop[id].Visibility = id.Equals(ini.DesktopProvider) ? Visibility.Visible : Visibility.Collapsed;
            //}
            //foreach (string id in dicPushLock.Keys) {
            //    dicPushLock[id].Visibility = id.Equals(ini.LockProvider) ? Visibility.Visible : Visibility.Collapsed;
            //}
            // 刷新主题设置
            RadioButton rbTheme = RbTheme.Items.Cast<RadioButton>().FirstOrDefault(rb => ini.Theme.Equals(rb.Tag));
            rbTheme.IsChecked = true;
            TextThemeCur.Text = rbTheme.Content.ToString();
            // 刷新“其他”组 Expander 随机一文
            _ = RandomGlitter();
            // 检查帐号
            _ = CheckAccount();
            // 展开当前图源 Expander
            Expander expanderFocus = null;
            foreach (var item in ViewSettings.Children) {
                if (item is Expander expander && expander.Tag != null) { // 图源 Expander
                    // 刷新指示图标
                    UIElementCollection icons = (expander.Header as Grid).Children;
                    (icons[1] as FontIcon).Visibility = expander.Tag.Equals(ini.Provider) ? Visibility.Visible : Visibility.Collapsed;
                    (icons[2] as FontIcon).Visibility = expander.Tag.Equals(ini.DesktopProvider) ? Visibility.Visible : Visibility.Collapsed;
                    (icons[3] as FontIcon).Visibility = expander.Tag.Equals(ini.LockProvider) ? Visibility.Visible : Visibility.Collapsed;
                    // 折叠非目标图源
                    if (expander.Tag.Equals(ini.Provider)) {
                        expanderFocus = expander;
                    } else {
                        expander.IsExpanded = false;
                    }
                }
            }
            if (expanderFocus != null) {
                expanderFocus.IsExpanded = true;
            }
        }

        public void NotifyPaneClosed() {
            // TODO
        }

        private void RefreshCateWhat(ComboBox box, ObservableCollection<Cate> list, GeneralIni gi) {
            if (list.Count > 0) {
                return;
            }
            foreach (string cate in gi.CateWhats) {
                list.Add(new Cate {
                    Id = cate,
                    Name = resLoader.GetString("CateWhat_" + cate)
                });
            }
            box.SelectedIndex = list.Select(t => t.Id).ToList().IndexOf(gi.CateWhat);
        }

        private async Task RefreshExpander(Expander expanderTarget) {
            // 刷新标题（标记为当前图源）、折叠非目标图源 Expander
            foreach (var item in ViewSettings.Children) {
                if (item is Expander expander && expander.Tag != null) {
                    FontIcon icon = ((expander.Header as Grid).Children[1] as FontIcon);
                    icon.Visibility = expander == expanderTarget ? Visibility.Visible : Visibility.Collapsed;
                    if (expander != expanderTarget && expander.IsExpanded) {
                        expander.IsExpanded = false;
                    }
                }
            }
            // 刷新图源类别设置项
            long start = DateTime.Now.Ticks;
            string providerId = expanderTarget.Tag as string;
            //ComboBox boxCate = ((expanderTarget.Content as StackPanel).Children[2] as Grid).Children[2] as ComboBox;
            //ObservableCollection<Cate> boxCateList = boxCate.ItemsSource as ObservableCollection<Cate>;
            //GeneralIni gi = ini.GetIni(providerId);
            //if (boxCateList.Count == 0) {
            //    foreach (string cate in gi.CateWhats) {
            //        boxCateList.Add(new Cate {
            //            Id = cate,
            //            Name = resLoader.GetString("CateWhat_" + cate)
            //        });
            //    }
            //    boxCate.SelectedIndex = boxCateList.Select(t => t.Id).ToList().IndexOf(gi.CateWhat);
            //}
            // 保存配置&回调
            if (!ini.Provider.Equals(providerId)) {
                ini.Provider = providerId;
                await IniUtil.SaveProviderAsync(providerId);
                SettingsChanged?.Invoke(this, new SettingsEventArgs {
                    ProviderChanged = true
                });
            }
            // 滚动至合适位置
            await Task.Delay(Math.Max(300 - (int)(DateTime.Now.Ticks - start) / 10000, 0));
            Point position = expanderTarget.TransformToVisual(ViewSettings).TransformPoint(new Point(0, 0));
            ScrollSettings.ChangeView(0, position.Y, 1, false);
        }

        private async Task RandomGlitter() {
            if (release == null) {
                release = await Api.VersionAsync();
                if (SysUtil.CheckNewVer(release.Version)) { // 回调提示新版本
                    SettingsChanged?.Invoke(this, new SettingsEventArgs {
                        VerOrBbsChanged = release
                    });
                } else if (release.Bbs != null) { // 回调提示新公告
                    foreach (BbsApiData bbs in release.Bbs) {
                        if (!localSettings.Values.ContainsKey("bbs-" + bbs.Id)) {
                            localSettings.Values["bbs-" + bbs.Id] = 1; // 只提示一次
                            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                                VerOrBbsChanged = release
                            });
                            break;
                        }
                    }
                }
            }
            long ticks = DateTime.Now.Ticks;
            // 刷新运营数据
            if (release.Life != null && release.Life.Past > 0) {
                if (release.Life.Remain >= 0) {
                    SettingsReviewDesc.Text = string.Format(resLoader.GetString("Life1"),
                        release.Life.Past, release.Life.Remain);
                } else {
                    SettingsReviewDesc.Text = string.Format(resLoader.GetString("Life0"),
                        release.Life.Past, -release.Life.Remain);
                }
            }
            // 刷新版本状态
            if (SysUtil.CheckNewVer(release.Version)) {
                TextRelease.Text = resLoader.GetString("NewRelease");
                LinkRelease.NavigateUri = new Uri(release.Url);
                ToolTipService.SetToolTip(LinkRelease, new ToolTip {
                    Content = release.Version
                });
            } else {
                TextRelease.Text = "";
            }
            // 刷新公告板
            if (release.Bbs != null && release.Bbs.Count > 0) {
                release.Bbs.Sort((a, b) => b.Start.CompareTo(a.Start)); // 日期降序
                RunBbsContent1.Text = release.Bbs[0].Comment;
                if (release.Bbs.Count > 1) {
                    RunBbsDate1.Text = string.Format(resLoader.GetString("BbsDate"),
                        DateTime.Parse(release.Bbs[0].Start).ToString("M"));
                    RunBbsContent2.Text = release.Bbs[1].Comment;
                    RunBbsDate2.Text = string.Format(resLoader.GetString("BbsDate"),
                        DateTime.Parse(release.Bbs[1].Start).ToString("M"));
                    TextBbsContent2.Visibility = Visibility.Visible;
                } else {
                    TextBbsContent2.Visibility = Visibility.Collapsed;
                }
                if (release.Bbs.Count > 2) {
                    RunBbsContent3.Text = release.Bbs[2].Comment;
                    RunBbsDate3.Text = string.Format(resLoader.GetString("BbsDate"),
                        DateTime.Parse(release.Bbs[2].Start).ToString("M"));
                    TextBbsContent3.Visibility = Visibility.Visible;
                } else {
                    TextBbsContent3.Visibility = Visibility.Collapsed;
                }
                ExpanderBbs.Visibility = Visibility.Visible;
            } else {
                ExpanderBbs.Visibility = Visibility.Collapsed;
            }
            // 随机一文
            GlitterApiData glitter = await Api.GlitterAsync(ini.Client);
            if (!string.IsNullOrEmpty(glitter?.Sentence)) {
                TextGlitterSentence.Text = glitter.Sentence;
                LinkGlitter.NavigateUri = new Uri("https://glitter.timeline.ink/" + glitter.Sentence);
            }
        }

        private async Task CheckAccount() {
            AccountApiData data = await Api.CheckAccountAsync(false, false, ini.User, ini.Pwd, ini.Client);
            if (!string.IsNullOrEmpty(ini.User) && data != null && data.Auth && data.Active) { // 已注册已登录已激活
                BtnAccount.Content = string.Format(resLoader.GetString("AccountUser"),
                    ini.User.Length > 10 ? ini.User.Substring(0, 10) : ini.User);
            } else {
                BtnAccount.Content = resLoader.GetString("BtnAccount/Content");
            }

            TextFavLimit.Visibility = string.IsNullOrEmpty(ini.User) || (data != null && !(data.Auth && data.Active))
                ? Visibility.Visible : Visibility.Collapsed;
            GeneralIni gi = ini.GetIni("glutton");
            TextGluttonLimit.Visibility = (string.IsNullOrEmpty(ini.User) || (data != null && !(data.Auth && data.Active))) && gi.Order == "date"
                ? Visibility.Visible : Visibility.Collapsed;
            gi = ini.GetIni("timeline");
            TextTimelineLimit.Visibility = (string.IsNullOrEmpty(ini.User) || (data != null && !(data.Auth && data.Active))) && gi.Order == "date"
                ? Visibility.Visible : Visibility.Collapsed;
            gi = ini.GetIni("bing");
            TextBingLimit.Visibility = (string.IsNullOrEmpty(ini.User) || (data != null && !(data.Auth && data.Active))) && gi.Order == "date"
                ? Visibility.Visible : Visibility.Collapsed;
            gi = ini.GetIni("nasa");
            TextNasaLimit.Visibility = (string.IsNullOrEmpty(ini.User) || (data != null && !(data.Auth && data.Active))) && gi.Order == "date"
                ? Visibility.Visible : Visibility.Collapsed;
            gi = ini.GetIni("one");
            TextOneLimit.Visibility = (string.IsNullOrEmpty(ini.User) || (data != null && !(data.Auth && data.Active))) && gi.Order == "date"
                ? Visibility.Visible : Visibility.Collapsed;
            TextLspLimit.Visibility = string.IsNullOrEmpty(ini.User) || (data != null && !(data.Auth && data.Active)) || (data != null && !data.R18)
                ? Visibility.Visible : Visibility.Collapsed;
            TextVenusLimit.Visibility = string.IsNullOrEmpty(ini.User) || (data != null && !(data.Auth && data.Active)) || (data != null && !data.R22)
                ? Visibility.Visible : Visibility.Collapsed;
        }

        private async Task ImportAsync() {
            BtnLocalImport.Content = resLoader.GetString("BtnLocalImport/Content");
            BtnLocalImport.IsEnabled = false;
            PbImport.IsIndeterminate = true;
            PbImport.ShowError = false;
            PbImport.ShowPaused = false;
            PbImport.Visibility = Visibility.Visible;
            GluttonProvider provider = ini.GenerateProvider("glutton") as GluttonProvider;
            GluttonIni gluttonIni = ini.GetIni("glutton") as GluttonIni;
            gluttonIni = new GluttonIni(gluttonIni.Id) {
                GalleryApi = gluttonIni.GalleryApi,
                Order = "date"
            };
            Go go = new Go(null);
            await provider.LoadData(new CancellationTokenSource().Token, ini, gluttonIni, go);
            LocalIni localIni = ini.GetIni("local") as LocalIni;
            List<Meta> top = provider.GetMetas().Take(localIni.Appetite).ToList();
            Dictionary<string, double> topProgress = new Dictionary<string, double>();
            if (top.Count == 0) {
                BtnLocalImport.IsEnabled = true;
                PbImport.ShowError = true;
                return;
            }
            BtnLocalImport.Content = string.Format(resLoader.GetString("ImportProgress"), 0, top.Count);
            PbImport.Maximum = top.Count;
            PbImport.Value = 0;

            BackgroundDownloader downloader = new BackgroundDownloader();
            IReadOnlyList<DownloadOperation> historyDownloads = await BackgroundDownloader.GetCurrentDownloadsAsync();
            StorageFolder folderLocal = await FileUtil.GetGalleryFolder(ini.Folder);
            if (folderLocal == null) {
                BtnLocalImport.IsEnabled = true;
                PbImport.ShowError = true;
                return;
            }
            foreach (Meta meta in top) { // 开始下载
                string localName = string.Format("{0}_{1}_{2}{3}",
                    AppInfo.Current.DisplayInfo.DisplayName,
                    resLoader.GetString("Provider_" + provider.Id),
                    meta.Id, meta.Format);
                if (await folderLocal.TryGetItemAsync(localName) != null || meta.Uhd == null) { // 已导入
                    await Task.Delay(60);
                    topProgress[meta.Uhd] = 1;
                    PbImport.Value = SumProgress(topProgress);
                    PbImport.IsIndeterminate = false;
                    BtnLocalImport.Content = string.Format(resLoader.GetString("ImportProgress"), (int)PbImport.Value, top.Count);
                    continue;
                }
                StorageFile cacheFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(
                    string.Format("{0}-{1}{2}", provider.Id, meta.Id, meta.Format), CreationCollisionOption.OpenIfExists);
                BasicProperties fileProperties = await cacheFile.GetBasicPropertiesAsync();
                if (fileProperties.Size > 0) { // 已缓存过
                    await cacheFile.CopyAsync(folderLocal, localName, NameCollisionOption.ReplaceExisting);
                    topProgress[meta.Uhd] = 1;
                    PbImport.Value = SumProgress(topProgress);
                    PbImport.IsIndeterminate = false;
                    BtnLocalImport.Content = string.Format(resLoader.GetString("ImportProgress"), (int)PbImport.Value, top.Count);
                    continue;
                }
                topProgress[meta.Uhd] = 0;
                foreach (DownloadOperation o in historyDownloads) { // 从历史中恢复任务
                    if (meta.Uhd.Equals(o.RequestedUri)) {
                        meta.Do = o;
                        break;
                    }
                }
                if (meta.Do == null) { // 新建下载任务
                    meta.Do = downloader.CreateDownload(new Uri(meta.Uhd), cacheFile);
                    _ = meta.Do.StartAsync();
                }
            }
            foreach (Meta meta in top) { // 等待下载
                if (meta.Do == null) {
                    continue;
                }
                try {
                    if (meta.Do.Progress.Status == BackgroundTransferStatus.PausedByApplication) {
                        meta.Do.Resume();
                    }
                    Progress<DownloadOperation> progress = new Progress<DownloadOperation>((op) => {
                        if (op.Progress.TotalBytesToReceive > 0 && op.Progress.BytesReceived > 0) {
                            ulong value = op.Progress.BytesReceived * 100 / op.Progress.TotalBytesToReceive;
                            //Debug.WriteLine(op.ResultFile.Name + " progress: " + value + "%");
                            topProgress[meta.Uhd] = op.Progress.BytesReceived * 1.0 / op.Progress.TotalBytesToReceive;
                            PbImport.Value = SumProgress(topProgress);
                            PbImport.IsIndeterminate = false;
                        }
                    });
                    _ = await meta.Do.AttachAsync().AsTask(progress);
                    if (meta.Do.Progress.Status == BackgroundTransferStatus.Completed) {
                        //Debug.WriteLine("ImportAsync() downloaded " + meta.Do.ResultFile.Name);
                        string localName = string.Format("{0}-{1}{2}", resLoader.GetString("Provider_" + provider.Id), meta.Id, meta.Format);
                        await meta.Do.ResultFile.CopyAsync(folderLocal, localName, NameCollisionOption.ReplaceExisting);
                        topProgress[meta.Uhd] = 1;
                        PbImport.Value = SumProgress(topProgress);
                        PbImport.IsIndeterminate = false;
                        BtnLocalImport.Content = string.Format(resLoader.GetString("ImportProgress"), (int)PbImport.Value, top.Count);
                    }
                } catch (Exception e) {
                    meta.Do = null;
                    LogUtil.E("ImportAsync() " + e.Message);
                }
            }
            BtnLocalImport.IsEnabled = true;
            PbImport.ShowPaused = true;
        }

        private double SumProgress(Dictionary<string, double> dicProgress) {
            double sum = 0;
            foreach (double v in dicProgress.Values) {
                sum += v;
            }
            return sum;
        }

        private async void ExpanderProvider_Expanding(Expander sender, ExpanderExpandingEventArgs args) {
            await RefreshExpander(sender);
        }

        private async void RbTheme_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            if (e.AddedItems.Count > 0 && e.AddedItems[0] is RadioButton selectItem) {
                TextThemeCur.Text = selectItem.Content.ToString();
                string theme = selectItem.Tag?.ToString();
                if (ini.Theme.Equals(theme)) {
                    return;
                }
                ini.Theme = theme;
                await IniUtil.SaveThemeAsync(ini.Theme);
                if (Window.Current.Content is FrameworkElement rootElement) {
                    rootElement.RequestedTheme = ThemeUtil.ParseTheme(theme);
                }
                SettingsChanged?.Invoke(this, new SettingsEventArgs {
                    ThemeChanged = true
                });
            }
        }

        private async void LinkReview_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args) {
            await FileUtil.LaunchUriAsync(new Uri(resLoader.GetString("UrlReview")));
        }

        private async void LinkDonate_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args) {
            await new DonateDlg {
                RequestedTheme = ThemeUtil.ParseTheme(ini.Theme) // 修复未响应主题切换的BUG
            }.ShowAsync();
        }

        private async void BtnDonate_Click(object sender, RoutedEventArgs e) {
            //await FileUtil.LaunchUriAsync(new Uri(resLoader.GetString("UrlReview")));
            await new DonateDlg {
                RequestedTheme = ThemeUtil.ParseTheme(ini.Theme) // 修复未响应主题切换的BUG
            }.ShowAsync();
        }

        private void BtnAccount_Click(object sender, RoutedEventArgs e) {
            //await new AccountDlg(ini).ShowAsync();
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ReqAccountDlg = true
            });
        }

        private async void BtnAbout_Click(object sender, RoutedEventArgs e) {
            await FileUtil.LaunchUriAsync(new Uri(resLoader.GetString("UrlAboutThank")));
        }

        private async void BtnIni_Click(object sender, RoutedEventArgs e) {
            if (!await FileUtil.LaunchFileAsync(await IniUtil.GetIniPath())) {
                await FileUtil.LaunchFolderAsync(ApplicationData.Current.LocalFolder, await IniUtil.GetIniPath());
            }
        }

        private async void BtnWeb_Click(object sender, RoutedEventArgs e) {
            string provider = ini.Provider;
            if ("local".Equals(provider) || "favorite".Equals(provider)) {
                provider = "snake";
            }
            string url = string.Format(resLoader.GetString("UrlWeb"), provider, ini.UserEncoded(), ini.PwdEncrypted());
            await FileUtil.LaunchUriAsync(new Uri(url));
        }

        private async void BtnShowCachePush_Click(object sender, RoutedEventArgs e) {
            //await FileUtil.LaunchFolderAsync(await StorageFolder.GetFolderFromPathAsync("%AppData%\\Microsoft\\Windows\\Themes\\CachedFiles"));
            await FileUtil.LaunchFolderAsync(await FileUtil.GetWallpaperFolderAsync());
        }

        private async void TbLocalOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridLocalOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("local");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private void BtnLocalFolder_Click(object sender, RoutedEventArgs e) {
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                LocalReqOpenFolder = true
            });
        }

        private async void BtnLocalPick_Click(object sender, RoutedEventArgs e) {
            FolderPicker picker = new FolderPicker();
            picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
            picker.FileTypeFilter.Add("*");

            StorageFolder folder = await picker.PickSingleFolderAsync();
            if (folder != null) {
                // Application now has read/write access to all contents in the picked folder
                // (including other sub-folder contents)
                StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", folder);

                //if (folder.Path.Equals(ini.Folder)) {
                //    return;
                //}
                ini.Folder = folder.Path;
                await IniUtil.SaveFolderAsync(ini.Folder);
                await IniUtil.SaveProviderAsync("local");
                SettingsChanged?.Invoke(this, new SettingsEventArgs {
                    ProviderConfigChanged = true
                });
            }
        }

        private async void BtnLocalImport_Click(object sender, RoutedEventArgs e) {
            await ImportAsync();
        }

        private void LinkAccount_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args) {
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ReqAccountDlg = true
            });
        }

        private async void TbFavoriteOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridFavoriteOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("favorite");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BtnFavoriteWeb_Click(object sender, RoutedEventArgs e) {
            bool active = false;
            if (!string.IsNullOrEmpty(ini.User)) {
                AccountApiData data = await Api.CheckAccountAsync(false, false, ini.User, ini.Pwd, ini.Client);
                if (data == null) {
                    return;
                }
                active = data.Auth && data.Active; // 已注册已登录已激活
            }
            if (active) {
                string url = string.Format(resLoader.GetString("UrlFavorite"), ini.UserEncoded(), ini.PwdEncrypted());
                await FileUtil.LaunchUriAsync(new Uri(url));
            } else {
                TmpFavDlg dlg = new TmpFavDlg { 
                    RequestedTheme = ThemeUtil.ParseTheme(ini.Theme) // 修复未响应主题切换的BUG
                };
                ContentDialogResult res = await dlg.ShowAsync();
                if (res == ContentDialogResult.Primary) {
                    SettingsChanged?.Invoke(this, new SettingsEventArgs {
                        ReqAccountDlg = true
                    });
                }
            }
        }

        private async void TbSnakeOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridSnakeOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("snake");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbSnakeCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridSnakeCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("snake");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxSnakeCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("snake");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbGluttonOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridGluttonOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            if (order != "date") {
                TextGluttonLimit.Visibility = Visibility.Collapsed;
            }
            GeneralIni gi = ini.GetIni("glutton");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbGluttonCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridGluttonCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("glutton");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxGluttonCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("glutton");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbTimelineOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridTimelineOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            if (order != "date") {
                TextTimelineLimit.Visibility = Visibility.Collapsed;
            }
            GeneralIni gi = ini.GetIni("timeline");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxTimelineCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("timeline");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbBingOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridBingOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            if (order != "date") {
                TextBingLimit.Visibility = Visibility.Collapsed;
            }
            GeneralIni gi = ini.GetIni("bing");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxBingCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("bing");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbNasaOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridNasaOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            if (order != "date") {
                TextNasaLimit.Visibility = Visibility.Collapsed;
            }
            GeneralIni gi = ini.GetIni("nasa");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbNasaCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridNasaCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("nasa");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxNasaCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("nasa");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbOneOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridOneOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            if (order != "date") {
                TextOneLimit.Visibility = Visibility.Collapsed;
            }
            GeneralIni gi = ini.GetIni("one");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbOneCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridOneCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("one");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxOneCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("one");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private void BoxHimawari8Offset_ValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args) {
            if (himawari8OffsetTimer == null) {
                himawari8OffsetTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 1000) };
                himawari8OffsetTimer.Tick += async (sender2, e2) => {
                    himawari8OffsetTimer.Stop();
                    float offset = (float)BoxHimawari8Offset.Value;
                    Himawari8Ini gi = ini.GetIni("himawari8") as Himawari8Ini;
                    if (Math.Abs(offset - gi.Offset) < 0.01f) {
                        return;
                    }
                    gi.Offset = offset;
                    await IniUtil.SaveHimawari8OffsetAsync(offset);
                    await IniUtil.SaveProviderAsync(gi.Id);
                    SettingsChanged?.Invoke(this, new SettingsEventArgs {
                        ProviderConfigChanged = true
                    });
                };
            }
            himawari8OffsetTimer.Stop();
            himawari8OffsetTimer.Start();
        }

        private void BoxHimawari8Ratio_ValueChanged(NumberBox sender, NumberBoxValueChangedEventArgs args) {
            if (himawari8RatioTimer == null) {
                himawari8RatioTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 1000) };
                himawari8RatioTimer.Tick += async (sender2, e2) => {
                    himawari8RatioTimer.Stop();
                    float ratio = (float)BoxHimawari8Ratio.Value;
                    Himawari8Ini gi = ini.GetIni("himawari8") as Himawari8Ini;
                    if (Math.Abs(ratio - gi.Ratio) < 0.01f) {
                        return;
                    }
                    gi.Ratio = ratio;
                    await IniUtil.SaveHimawari8RatioAsync(ratio);
                    await IniUtil.SaveProviderAsync(gi.Id);
                    SettingsChanged?.Invoke(this, new SettingsEventArgs {
                        ProviderConfigChanged = true
                    });
                };
            }
            himawari8RatioTimer.Stop();
            himawari8RatioTimer.Start();
        }

        private async void TbQingbzOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridQingbzOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("qingbz");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbQingbzCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridQingbzCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("qingbz");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxQingbzCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("qingbz");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbZzzmhOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridZzzmhOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("zzzmh");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbZzzmhCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridZzzmhCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("zzzmh");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxZzzmhCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("zzzmh");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbNetbianOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridNetbianOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("netbian");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbNetbianCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridNetbianCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("netbian");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxNetbianCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("netbian");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbToopicOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridToopicOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("toopic");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbToopicCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridToopicCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("toopic");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxToopicCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("toopic");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbWallhavenOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridWallhavenOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("wallhaven");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbWallhavenCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridWallhavenCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("wallhaven");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxWallhavenCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("wallhaven");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbWallhereOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridWallhereOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("wallhere");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbWallhereCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridWallhereCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("wallhere");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxWallhereCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("wallhere");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbWallpaperupOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridWallpaperupOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("wallpaperup");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbWallpaperupCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridWallpaperupCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("wallpaperup");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxWallpaperupCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("wallpaperup");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbAbyssOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridAbyssOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("abyss");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbAbyssCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridAbyssCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("abyss");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxAbyssCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("abyss");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void Tb3gOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in Grid3gOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("3g");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void Tb3gCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in Grid3gCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("3g");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void Box3gCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("3g");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbHuamaoOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridHuamaoOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("huamao");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbHuamaoCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridHuamaoCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("huamao");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxHuamaoCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("huamao");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbPexelsOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridPexelsOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("pexels");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxPexelsCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("pexels");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbColorhubOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridColorhubOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("colorhub");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxColorhubCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("colorhub");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbSimpleOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridSimpleOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("simple");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxSimpleCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("simple");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbEsoOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridEsoOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("eso");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbEsoCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridEsoCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("eso");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxEsoCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("eso");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbYmyouliOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridYmyouliOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("ymyouli");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbYmyouliCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridYmyouliCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("ymyouli");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void BoxYmyouliCateWhat_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            string cate = (e.AddedItems[0] as Cate).Id;
            GeneralIni gi = ini.GetIni("ymyouli");
            if (cate.Equals(gi.CateWhat)) {
                return;
            }
            gi.CateWhat = cate;
            await IniUtil.SaveProviderCateWhatAsync(gi.Id, gi.CateWhat);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbLspOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridLspOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("lsp");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbLspCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridLspCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("lsp");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbVenusOrder_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridVenusOrder.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string order = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("venus");
            if (order.Equals(gi.Order)) {
                return;
            }
            gi.Order = order;
            await IniUtil.SaveProviderOrderAsync(gi.Id, gi.Order);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private async void TbVenusCateHow_Click(object sender, RoutedEventArgs e) {
            ToggleButton tbThis = sender as ToggleButton;
            foreach (ToggleButton tb in GridVenusCateHow.Children.Cast<ToggleButton>()) {
                tb.IsChecked = tb.Tag.Equals(tbThis.Tag);
            }
            string cate = tbThis.Tag as string;
            GeneralIni gi = ini.GetIni("venus");
            if (cate.Equals(gi.CateHow)) {
                return;
            }
            gi.CateHow = cate;
            await IniUtil.SaveProviderCateHowAsync(gi.Id, gi.CateHow);
            await IniUtil.SaveProviderAsync(gi.Id);
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                ProviderConfigChanged = true
            });
        }

        private void LinkApplyR22_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args) {
            SettingsChanged?.Invoke(this, new SettingsEventArgs {
                VenusReqR22Dlg = true
            });
        }

        private string GenerateProviderTitle(object tag) {
            // 生成图源 Expander 标题
            return resLoader.GetString("Provider_" + tag);
        }

        private string GenerateProviderDesc(object tag) {
            // 生成图源 Expander 描述
            return resLoader.GetString("Slogan_" + tag);
        }

        //private string GenerateProviderIcon(bool expanded) {
        //    // 无聊：根据图源 Expander 展开状态显示不同的图标
        //    // 注意，XAML：&#xE899; C#：\uE899
        //    return expanded ? "\uE899" : "\uE76E";
        //}

        //private string GeneratePushDesktopIcon(object tag) {
        //    Debug.WriteLine("GeneratePushDesktopIcon() " + ini.DesktopProvider);
        //    return tag.Equals(ini.DesktopProvider) ? "\uE7F7" : "";
        //}

        //private string GeneratePushLockIcon(object tag) {
        //    Debug.WriteLine("GeneratePushLockIcon() " + ini.LockProvider);
        //    return tag.Equals(ini.LockProvider) ? "\uEE3F" : "";
        //}
    }

    public class SettingsEventArgs : EventArgs {
        public bool ProviderChanged { get; set; }

        public bool ProviderConfigChanged { get; set; }

        public bool ThemeChanged { get; set; }

        public ReleaseApiData VerOrBbsChanged { get; set; }

        public bool ReqAccountDlg { get; set; }

        public bool LocalReqOpenFolder { get; set; }

        public bool VenusReqR22Dlg { get; set; }
    }
}
